package drds.global_transaction;

import org.feisoft.common.utils.ByteUtils;
import org.feisoft.transaction.xa.TransactionId;
import org.feisoft.transaction.xa.XidFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.NetworkInterface;
import java.util.Enumeration;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

public class TransactionIdFactory implements XidFactory
{
    static final Logger logger = LoggerFactory.getLogger(TransactionIdFactory.class);

    static final int SIZE_OF_MAC = 6;
    static final Random random = new Random();
    static final byte[] hardwareAddress = new byte[SIZE_OF_MAC];
    static final AtomicInteger atomic = new AtomicInteger();

    static
    {
        byte[] sourceByteArray = getHardwareAddress();
        System.arraycopy(sourceByteArray, 0, hardwareAddress, 0, SIZE_OF_MAC);
    }

    private static byte[] getHardwareAddress()
    {
        Enumeration<NetworkInterface> enumeration = null;
        try
        {
            enumeration = NetworkInterface.getNetworkInterfaces();
        } catch (Exception ex)
        {
            logger.debug(ex.getMessage(), ex);
        }

        byte[] bytes = null;
        while (bytes == null && enumeration != null && enumeration.hasMoreElements())
        {
            NetworkInterface networkInterface = enumeration.nextElement();

            try
            {
                if (networkInterface.isUp() == false)
                {
                    continue;
                } else if (networkInterface.isPointToPoint() || networkInterface.isVirtual())
                {
                    continue;
                } else if (networkInterface.isLoopback())
                {
                    continue;
                }

                byte[] hardwareAddress = networkInterface.getHardwareAddress();
                if (hardwareAddress == null || hardwareAddress.length != SIZE_OF_MAC)
                {
                    continue;
                }
                bytes = new byte[SIZE_OF_MAC];
                System.arraycopy(hardwareAddress, 0, bytes, 0, SIZE_OF_MAC);
            } catch (Exception rex)
            {
                logger.debug(rex.getMessage(), rex);
            }
        }

        return bytes != null ? bytes : new byte[SIZE_OF_MAC];
    }

    public TransactionId createGlobalTransactionId()
    {
        byte[] global = new byte[GLOBAL_TRANSACTION_LENGTH];

        long millis = System.currentTimeMillis();
        byte[] millisByteArray = ByteUtils.longToByteArray(millis);

        short value = (short) (atomic.incrementAndGet() % Short.MAX_VALUE);
        byte[] valueByteArray = ByteUtils.shortToByteArray(value);

        byte[] randomByteArray = new byte[4];
        random.nextBytes(randomByteArray);

        System.arraycopy(hardwareAddress, 0, global, 0, SIZE_OF_MAC);
        System.arraycopy(millisByteArray, 0, global, SIZE_OF_MAC, 8);
        System.arraycopy(valueByteArray, 0, global, SIZE_OF_MAC + 8, 2);
        System.arraycopy(randomByteArray, 0, global, SIZE_OF_MAC + 10, randomByteArray.length);

        return new TransactionId(XidFactory.JTA_FORMAT_ID, global);
    }

    public TransactionId createGlobalTransactionId(byte[] globalTransactionId)
    {
        if (globalTransactionId == null)
        {
            throw new IllegalArgumentException("The globalTransactionId cannot be null.");
        } else if (globalTransactionId.length > TransactionId.MAXGTRIDSIZE)
        {
            throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
        }
        byte[] global = new byte[globalTransactionId.length];
        System.arraycopy(globalTransactionId, 0, global, 0, global.length);
        return new TransactionId(XidFactory.JTA_FORMAT_ID, global);
    }

    public TransactionId createBranchTransactionId(TransactionId globalTransactionId)
    {
        if (globalTransactionId == null)
        {
            throw new IllegalArgumentException("Xid cannot be null.");
        } else if (globalTransactionId.getGlobalTransactionId() == null)
        {
            throw new IllegalArgumentException("The globalTransactionId cannot be null.");
        } else if (globalTransactionId.getGlobalTransactionId().length > TransactionId.MAXGTRIDSIZE)
        {
            throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
        }

        byte[] global = new byte[globalTransactionId.getGlobalTransactionId().length];
        System.arraycopy(globalTransactionId.getGlobalTransactionId(), 0, global, 0, global.length);

        byte[] branch = new byte[BRANCH_QUALIFIER_LENGTH];
        long millis = System.currentTimeMillis();
        byte[] millisByteArray = ByteUtils.longToByteArray(millis);

        short value = (short) (atomic.incrementAndGet() % Short.MAX_VALUE);
        byte[] valueByteArray = ByteUtils.shortToByteArray(value);

        byte[] randomByteArray = new byte[4];
        random.nextBytes(randomByteArray);

        System.arraycopy(hardwareAddress, 0, branch, 0, SIZE_OF_MAC);
        System.arraycopy(millisByteArray, 0, branch, SIZE_OF_MAC, 8);
        System.arraycopy(valueByteArray, 0, branch, SIZE_OF_MAC + 8, 2);
        System.arraycopy(randomByteArray, 0, branch, SIZE_OF_MAC + 10, randomByteArray.length);

        return new TransactionId(XidFactory.JTA_FORMAT_ID, global, branch);
    }

    public TransactionId createBranchTransactionId(TransactionId globalTransactionId, byte[] branchQualifier)
    {
        if (globalTransactionId == null)
        {
            throw new IllegalArgumentException("Xid cannot be null.");
        } else if (globalTransactionId.getGlobalTransactionId() == null)
        {
            throw new IllegalArgumentException("The globalTransactionId cannot be null.");
        } else if (globalTransactionId.getGlobalTransactionId().length > TransactionId.MAXGTRIDSIZE)
        {
            throw new IllegalArgumentException("The length of globalTransactionId cannot exceed 64 bytes.");
        }

        if (branchQualifier == null)
        {
            throw new IllegalArgumentException("The branchQulifier cannot be null.");
        } else if (branchQualifier.length > TransactionId.MAXBQUALSIZE)
        {
            throw new IllegalArgumentException("The length of branchQulifier cannot exceed 64 bytes.");
        }

        byte[] global = new byte[globalTransactionId.getGlobalTransactionId().length];
        System.arraycopy(globalTransactionId.getGlobalTransactionId(), 0, global, 0, global.length);

        return new TransactionId(XidFactory.JTA_FORMAT_ID, global, branchQualifier);
    }


}
